package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.ModelInfo;
import cn.mw.cmdb.entity.modelRelation.*;
import cn.mw.cmdb.mapper.ModelAssetMapper;
import cn.mw.cmdb.mapper.ModelRelateMapper;
import cn.mw.cmdb.mongoMapper.RelationGroupMapper;
import cn.mw.cmdb.service.ModelRelationService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.util.ModelUtils;
import cn.mw.components.mongodb.exception.BusinessException;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.util.ListMapObjUtils;
import cn.mw.microMonitorCommon.utils.DatabaseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.driver.Driver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
public class ModelRelationServiceImpl implements ModelRelationService {

    @Autowired
    private Driver driver;

    @Autowired
    private CmdbServiceManage cmdbServiceManage;

    @Autowired
    private RelationGroupMapper relationGroupMapper;

    @Autowired
    private ModelAssetMapper modelAssetMapper;

    @Autowired
    private ModelRelateMapper modelRelateMapper;

    static final String UNGROUP_NAME = "未分组";
    static final String UNGROUP_DESC = "自动创建未分组";

    @Override
    public ModelRelationView getModelRelation(String modelId) throws Exception {
        //封装返回信息
        ModelRelationView modelRelationView = new ModelRelationView();
        CustomModelServiceImpl customModelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
        ModelInfo modelInfo = customModelService.selectById(modelId);

        List<RelationGroupDTO> groupList = relationGroupMapper.find(new Query(Criteria.where(CMDBTool.getFieldName(
                RelationGroupDTO::getOwnModelId)).is(modelId)));
        ModelAsset modelAsset = null;
        //查询已有节点信息
        Optional<ModelAsset> existNode = modelAssetMapper.findById(modelId);
        if (existNode.isPresent()) {
            modelAsset = existNode.get();
        }
        modelRelationView.extractFrom(modelAsset, groupList, modelInfo);
        return modelRelationView;
    }

    @Override
    public RelationGroupDTO creatModelRelationsGroup(RelationGroupDTO param) {
        param.setCreater(SecurityUtils.getUser().getId());
        param.setCreateTime(new Date());
        relationGroupMapper.save(param);
        return param;
    }

    @Override
    public RelationGroupDTO editModelRelationsGroup(RelationGroupDTO param) throws Exception {
        param.setModifier(SecurityUtils.getUser().getId());
        param.setModificationTime(new Date());
        Query query = new Query(CriteriaWrapper.where(RelationGroupDTO::getId).is(param.getId()));
        Update update = CMDBTool.genMongoUpdate(param, null, null);
        CMDBTool.updateFirst(relationGroupMapper, query, update);
        return param;
    }

    @Override
    public Boolean deleteModelRelationsGroup(String groupId) throws Exception {
        //删除之前，先判断分组下是否有关系数据存在
        RelationGroupDTO relationGroupDTO = relationGroupMapper.findById(groupId);
        if (relationGroupDTO == null) {
            return true;
        }
        boolean isExistRelation = false;
        Optional<ModelAsset> existNode = modelAssetMapper.findById(relationGroupDTO.getOwnModelId());
        ModelAsset modelAsset = null;
        if (existNode.isPresent()) {
            modelAsset = existNode.get();
        }
        //检查分组下是否存在关系
        if (null != modelAsset) {
            List<ModelRelate> modelRelateList = modelAsset.getOutModelRelates();
            if (null != modelRelateList) {
                for (ModelRelate modelRelate : modelRelateList) {
                    ModelRelationDTO modelRelationDTO = modelRelate.getModelRelationDTO();
                    if (modelRelationDTO.getGroupIds().contains(groupId)) {
                        isExistRelation = true;
                        break;
                    }
                }
            }
        }

        if (!isExistRelation) {
            relationGroupMapper.remove(new Query(CriteriaWrapper.where(RelationGroupDTO::getId).is(groupId)));
        } else {
            throw new BusinessException("model-relation-group-delete-check");
        }
        return true;
    }

    @Override
    public RelationGroupDTO getRelationGroupInfoById(String groupId) {
        return relationGroupMapper.findById(groupId);
    }

    @Override
    public List<RelationGroupDTO> getRelationGroupInfoByModelId(String modelId) {
        Query query = new Query(Criteria.where(DatabaseUtils.getFieldName(RelationGroupDTO::getOwnModelId)).is(modelId));
        return relationGroupMapper.find(query);
    }

    @Override
    public Boolean creatModelRelation(AddAndUpdateModelRelationParam param) throws Exception {
        //查询已有节点信息
        Optional<ModelAsset> existNode = modelAssetMapper.findById(param.getOwnModelId());
        ModelAsset modelAsset = null;
        if (existNode.isPresent()) {
            modelAsset = existNode.get();
        }
        if (null == modelAsset) {
            modelAsset = new ModelAsset();
            modelAsset.setId(param.getOwnModelId());
            doCreate(modelAsset, param);
        } else {
            //检查关系是否已经存在
            String newRelationKey = ModelUtils.genRelationKey(param.getOwnModelId().toString(), param.getOppositeModelId().toString());
            boolean isExist = false;
            List<ModelRelate> modelRelateList = modelAsset.getOutModelRelates();
            if (null != modelRelateList) {
                for (ModelRelate modelRelate : modelRelateList) {
                    String key = ModelUtils.genRelationKey(param.getOwnModelId().toString()
                            , modelRelate.getEndNode().getId().toString());
                    if (newRelationKey.equals(key)) {
                        isExist = true;
                        break;
                    }
                }
            }

            if (!isExist) {
                doCreate(modelAsset, param);
            } else {
                throw new BusinessException("model-relation-add-check-duplicate");
            }
        }
        return true;
    }

    @Override
    public Boolean editModelRelation(AddAndUpdateModelRelationParam param) throws Exception {
        //查询已有节点信息
        Optional<ModelAsset> existNode = modelAssetMapper.findById(param.getOwnModelId());
        ModelAsset modelAsset = null;
        if (existNode.isPresent()) {
            modelAsset = existNode.get();
        }
        if (null != modelAsset && null != modelAsset.getAllRelates()) {
            //找到需要修改的关系
            ModelRelate selectModelRelate = null;
            for (ModelRelate modelRelate : modelAsset.getAllRelates()) {
                if (modelRelate.getEndNode().getId().equals(param.getOppositeModelId())) {
                    selectModelRelate = modelRelate;
                    break;
                }
            }

            ModelRelationDTO modelRelationDTO = selectModelRelate.getModelRelationDTO();
            List<RelationGroupDTO> groupList = relationGroupMapper.find(new Query(Criteria.where(CMDBTool.getFieldName(
                    RelationGroupDTO::getOwnModelId)).is(param.getOwnModelId())));
            List<String> existsGroupIds = groupList.stream().map(RelationGroupDTO::getId).toList();
            if (null != modelRelationDTO) {
                modelRelationDTO.getRelationInfoMap().clear();
                ModelRelationInfo modelRelationInfo = ModelUtils.genOwnModelRelationInfo(param);
                modelRelationDTO.addModelRelationInfo(modelRelationInfo);
                modelRelationInfo = ModelUtils.genOppoModelRelationInfo(param);
                modelRelationDTO.addModelRelationInfo(modelRelationInfo);
                // 修改分组
                for (int i = 0; i < modelRelationDTO.getGroupIds().size(); i++) {
                    if (existsGroupIds.contains(modelRelationDTO.getGroupIds().get(i))) {
                        modelRelationDTO.getGroupIds().remove(i);
                        modelRelationDTO.getGroupIds().add(i, param.getRelationGroupId());
                        break;
                    }
                }
            }

            modelAssetMapper.save(modelAsset);
        }
        return true;
    }

    @Override
    public Boolean deleteModelRelation(AddAndUpdateModelRelationParam param) throws Exception {
        //查询已有节点信息
        Optional<ModelAsset> existNode = modelAssetMapper.findById(param.getOwnModelId());
        if (existNode.isPresent()) {
            ModelAsset modelAsset = existNode.get();
            //检查关系是否已经存在
            String newRelationKey = ModelUtils.genRelationKey(param.getOwnModelId().toString(), param.getOppositeModelId().toString());
            List<ModelRelate> modelRelateList = modelAsset.getAllRelates();
            if (null != modelRelateList) {
                for (ModelRelate modelRelate : modelRelateList) {
                    String key = ModelUtils.genRelationKey(param.getOwnModelId().toString()
                            , modelRelate.getEndNode().getId().toString());
                    if (newRelationKey.equals(key)) {
                        modelRelateMapper.deleteByRelateId(modelRelate.getRelationId());
                    }
                }
            }
        }
        return true;
    }

    @Override
    public AddAndUpdateModelRelationParam getModelRelationDetail(AddAndUpdateModelRelationParam param) throws Exception {
        //查询已有节点信息
        Optional<ModelAsset> existNode = modelAssetMapper.findById(param.getOwnModelId());
        ModelAsset startModel = null;
        if (existNode.isPresent()) {
            startModel = existNode.get();
        }
        if (null != startModel) {
            ModelRelate selectModelRelate = null;
            // 先从下级模型关系查找
            if (null != startModel.getOutModelRelates()) {
                for (ModelRelate modelRelate : startModel.getOutModelRelates()) {
                    if (modelRelate.getEndNode().getId().equals(param.getOppositeModelId())) {
                        selectModelRelate = modelRelate;
                        break;
                    }
                }
            }

            // 下级模型关系未找到时找上级关系
            if (selectModelRelate == null && null != startModel.getInModelRelates()) {
                for (ModelRelate modelRelate : startModel.getInModelRelates()) {
                    if (modelRelate.getEndNode().getId().equals(param.getOppositeModelId())) {
                        selectModelRelate = modelRelate;
                        break;
                    }
                }
            }

            if (null != selectModelRelate) {
                {
                    String ownModelId = param.getOwnModelId();
                    String oppoModelId = param.getOppositeModelId();
                    //查询分组信息
                    Query query = new Query(Criteria.where(DatabaseUtils.getFieldName(RelationGroupDTO::getOwnModelId)).is(ownModelId));
                    List<RelationGroupDTO> groupInfoList = relationGroupMapper.find(query);
                    CustomModelServiceImpl customModelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
                    //查询本端模型信息
                    ModelInfo ownModelInfo = customModelService.selectById(ownModelId);
                    ModelInfo oppoModelInfo = customModelService.selectById(oppoModelId);

                    //找到本端模型id对应的关系组信息
                    ModelRelationDTO modelRelationDTO = selectModelRelate.getModelRelationDTO();
                    if (null != modelRelationDTO) {
                        RelationGroupDTO ownGroup = null;
                        for (RelationGroupDTO relationGroupDTO : groupInfoList) {
                            if (modelRelationDTO.getGroupIds().contains(relationGroupDTO.getId())) {
                                ownGroup = relationGroupDTO;
                                break;
                            }
                        }

                        Map map = (Map) modelRelationDTO.getRelationInfoMap().get(ownModelId);
                        ModelRelationInfo ownModelRelationInfo = ListMapObjUtils.mapToBean(map, ModelRelationInfo.class);

                        map = (Map) modelRelationDTO.getRelationInfoMap().get(oppoModelId);
                        ModelRelationInfo oppoModelRelationInfo = ListMapObjUtils.mapToBean(map, ModelRelationInfo.class);

                        AddAndUpdateModelRelationParam info = new AddAndUpdateModelRelationParam();
                        info.setRelationGroupId(ownGroup.getId());
                        info.setOwnModelId(ownModelId);
                        info.setOwnModelName(ownModelInfo.getModelName());
                        info.setOwnRelationNum(ownModelRelationInfo.getNum());
                        info.setOwnRelationName(ownModelRelationInfo.getRelationName());
                        info.setOwnRelationId(ownModelRelationInfo.getId());

                        info.setOppositeModelId(oppoModelId);
                        info.setOppositeModelName(oppoModelInfo.getModelName());
                        info.setOppositeRelationNum(oppoModelRelationInfo.getNum());
                        info.setOppositeRelationName(oppoModelRelationInfo.getRelationName());
                        info.setOppositeRelationId(oppoModelRelationInfo.getId());
                        return info;
                    }
                }
            }
        }
        return null;
    }

    private void doCreate(ModelAsset start, AddAndUpdateModelRelationParam param) throws Exception {

        ModelAsset oppoModelAsset = new ModelAsset();
        oppoModelAsset.setId(param.getOppositeModelId());

        ModelRelate modelRelate = new ModelRelate();
        start.addOutModelRelate(modelRelate);

        ModelAsset startNode = new ModelAsset();
        startNode.extractFrom(start);
        modelRelate.setEndNode(oppoModelAsset);

        ModelRelationDTO modelRelationDTO = new ModelRelationDTO();
        ModelRelationInfo modelRelationInfo = ModelUtils.genOwnModelRelationInfo(param);
        modelRelationDTO.addModelRelationInfo(modelRelationInfo);
        modelRelationDTO.addGroupId(param.getRelationGroupId());

        //查询对端模型id对应的默认分组是否存在,如果不存在则创建
        //由于对端此时并未设置关系所对应的分组,此时应放到默认分组下
        List<RelationGroupDTO> defaultGroupList = relationGroupMapper.find(new Query(
                CriteriaWrapper.where(RelationGroupDTO::getOwnModelId).is(param.getOppositeModelId())
                        .andOperator(CriteriaWrapper.where(RelationGroupDTO::isDefautGroupFlag).is(true))));
        if (CollectionUtils.isEmpty(defaultGroupList)) {
            RelationGroupDTO params = new RelationGroupDTO();
            params.setOwnModelId(param.getOppositeModelId());
            params.setRelationGroupName(UNGROUP_NAME);
            params.setRelationGroupDesc(UNGROUP_DESC);
            params.setDefautGroupFlag(true);
            creatModelRelationsGroup(params);
            modelRelationDTO.addGroupId(params.getId());
        } else {
            modelRelationDTO.addGroupId(defaultGroupList.get(0).getId());
        }

        modelRelationInfo = ModelUtils.genOppoModelRelationInfo(param);
        modelRelationDTO.addModelRelationInfo(modelRelationInfo);
        modelRelate.setModelRelationDTO(modelRelationDTO);

        modelAssetMapper.save(start);
    }

    private String genDirectRelateModelById(String ret) {
        StringBuffer sb = new StringBuffer("match (").append(ModelUtils.OWN_KEY).append(":ModelAsset)")
                .append("-[").append(ModelUtils.RELATION_KEY).append(":RelateModel]")
                .append("-(").append(ModelUtils.OPPO_KEY).append(":ModelAsset)")
                .append(" where ").append(ModelUtils.OWN_KEY).append(".id = $id");
        if (StringUtils.isNotEmpty(ret)) {
            sb.append(ret);
        } else {
            sb.append(" return ").append(ModelUtils.OWN_KEY)
                    .append(",").append(ModelUtils.RELATION_KEY).append(",").append(ModelUtils.OPPO_KEY)
            ;
        }
        return sb.toString();
    }
}
